1. Field of the Invention
The present invention relates to data processing systems, and more particularly to the debugging of a device driver used by such systems.
2. Description of the Related Art
Typically a data processing system will comprise a system unit including a random access memory (RAM) a read only store (ROS), a central processing unit (CPU) and a mass storage device such as a hard disk drive, all interconnected via a system bus. The system will run in conjunction with an operating system. Examples of such are AIX from the IBM Corporation and Windows 98 from the Microsoft Corporation. The operating system comprises code responsible for controlling the internal functions of the computer and most importantly for allowing the user to run application software and to access the facilities provided by the system. These include those which provide access to peripherals or hardware components, such as printers, modems, adapter cards etc.
Within the operating system resides a small portion of intensively used code known as the kernel. This provides services such as memory and I/O management etc. to application programs. A device driver is an integral part of the kernel itself and is a piece of code which the computer uses to control a peripheral component by transferring data between the kernel and that component. The driver is often intensively used by applications requiring access to a device (eg., a printer) and therefore the more time apportioned by the driver to a particular application, the less time available for its use by other programs in the system.
As the processing power and capability of computers increases, so does the expectation of users. Performance is crucial and the speed and efficiency at which device drivers operate is important.
With such intensive use, it is not uncommon for a problem to occur with the driver which needs diagnosing. In such a demanding environment, it is vital that problems are dealt with quickly and effectively. The process of diagnosing and eliminating errors from any piece of code is known as debugging. Developers have adopted a number of different approaches to this, each with varying degrees of success.
The use of formal proofs and methods is one such example. This involves comparing inputs to the program and outputs produced upon execution with the specification against which the code was tested prior to implementation. However, this is a manual and laborious process which relies upon human attention to detail and does not generate tracing at run-time. It is not therefore, possible to follow the program through its execution path and determine the point at which the problem occurs.
A number of software tools also exist, aimed at producing xe2x80x98bug freexe2x80x99 code. These provide an environment from within which breakpoints can be set, enabling one to halt the program at certain points during its execution and to manipulate its values in-situ. Although this method will ultimately produce working code, it is not suitable for complex programs which have time-dependencies and are performance-critical.
Programmers will also hard-code trace statements into the source in order that these are generated at run-time upon the execution of a particular logic unit. A print statement will produce valuable trace information. However, there is no means for switching this information off and this has performance overheads. This method is therefore not appropriate for time-dependent applications and furthermore, the production of such additional screen output may not be acceptable to some users.
A slight improvement upon the above process is the ability to generate run-time conditional-on trace statements. It is possible to switch trace on/off during program execution and this can be done via a boolean flag under the control of a tool external to the application. Each trace-point has an xe2x80x9cifxe2x80x9d expression associated with it and the flag is continually evaluated via this expression at run-time:
However, the performance costs of such an evaluation may be unacceptable in itself, since it makes it impossible for such a program to operate optimally. Those applications which demonstrate time-dependent behaviour require a greater degree of efficiency. Such a method is nevertheless useful since it provides the user with a greater degree of dynamic control.
The ability to generate compile-time conditional-on trace statements provides yet a further improvement. The approach is similar to that described above, except that the xe2x80x9cifxe2x80x9d expression is replaced by the macro, #ifdef . . . #endif:
This permits compile-time evaluation and allows for the production of two versions of the code:
i) The code is optimised and no trace information output; or
ii) Trace statements are permanently on
Which of the two is produced is dependent upon the setting of the compile_time_debug_flag under the control of the build process. A setting of 0 produces no trace information i), whereas a setting of 1 results in ii) above. This method does not however, allow one to turn trace on and off dynamically. Therefore, if whilst running in xe2x80x9cdebug modexe2x80x9d the problem is fixed and no more trace output necessary, there is no means of stopping the information being displayed without unloading the driver and replacing it with the optimum version. This is both time-consuming and impractical.
A hybrid of xe2x80x9ccompile-time conditional-onxe2x80x9d and xe2x80x9crun-time conditional-onxe2x80x9d trace statements can prove more useful. This approach will produce one version of code with no trace statements in for optimal performance and a second version of the code with run-time conditional-on trace statements. The second version permits trace to be dynamically switched on and off. There is, however, a performance overhead associated with this, since the boolean flag still has to be continually evaluated at run-time.
Such an approach provides a version of the code which can run to optimal performance, eliminating completely any tracing overheads. Furthermore, it supplies the flexibility to run a debug-version of the same piece of code when a problem has been discovered. This strategy is commonly used in applications where the overall efficiency of the code is important. However, this approach does not permit a transparent switch between the two versions of code. One device driver has to be unloaded before the other can be used. It is not therefore ideally suited for use with device drivers, since these are used intensively by applications. Additionally, they are difficult to substitute and it is frequently necessary for the system to be taken down and rebooted before a new driver can be re-loaded in place of the old one.
Whilst the AIX operating system from the IBM Corporation does permit a device driver to be removed from the kernel and re-loaded if necessary, this process is not transparent to the applications using the driver. All applications must first close the file handles associated with the device driver. Thus although there is no need to take the whole system down, continuous service is still interrupted.
From the technicians point of view, the act of closing the product down and replacing the device driver with a debug version in order to perform diagnostics is not only time-consuming, but will often remove the problem from view. It may be difficult to recreate the same problem situation.
U.S. Pat. No. 4,802,165, issued Jan. 31, 1989 discloses a method and apparatus for debugging a computer program. It enables one to select/deselect macros from the command line, which when selected expand to include operable tracing code. However, it does not allow one to permanently deselect all macros in order to produce optimised code, without recompilation.
U.S. Pat. No. 5,815,707, issued Sep. 29, 1998 discloses a method for implementing dynamic function replacement in a STREAMs environment. This too permits a function, providing tracing facilities, to be incorporated into an optimised device driver, whilst still affording applications using the driver continuous service. However, this prior art requires the additional overhead of dynamic function replacement data structures and so is therefore not an option for systems incapable of operating in such an environment.
Accordingly the invention provides a method for executing a software component in a computer system, wherein said software component is used by one or more applications running on the computer system, and wherein said method comprises: providing a first and second version of said software component, wherein said first version of the software component is optimised and said second version contains debug code for providing trace information; and selectively switching between the first and second versions of said software component, said switching being transparent to any applications already using the software component.
In the preferred embodiment the software component is a device driver and an application using the device driver first establishes communication therewith by making an appropriate call to the computer operating system. Conventionally, device drivers form part of the operating system within a computer and so are difficult to remove and re-load without taking the whole system down and re-booting. The invention however affords continuous service to any application already using the device driver and does not require a time-wasting reboot which may in any event remove the error condition.
The invention is also applicable to general system libraries and furthermore to a server running a critical, intensively used service which cannot be spared whilst the service is xe2x80x9ctaken-downxe2x80x9d in order to load diagnostic code.
The first version of the driver is used by default by an application upon the execution of said software component and the switching between the first and second versions of the software component occurs in response to a system call. Such a method provides the flexibility to turn trace on and off dynamically, enabling optimum performance unless trace is required. The system call sets a boolean flag and it is the value of said boolean flag that determines which version of said software component is executed.
In a further aspect, the invention provides a method for constructing a software component, comprising the steps of: creating source code; preprocessing said source code to produce two versions of said code, wherein said second version differs from said first version in that said second version contains debug code for providing trace information; combining said first and second versions to construct the software component.
In the preferred embodiment, one version of the driver is created automatically from the other version by copying and hence the software component provides corresponding functions in both versions. The function names in one version however are modified with an identifier which distinguishes the function names in that version from the other version, thereby allowing the two sets of functions to coexist in the same software component.
The debug code may be removed either during pre-processing from the second version in order to produce the first version, or by use of a special compile flag.
The construction of the software component further comprises compiling the first and second versions separately to produce two object modules; and linking both object modules with a third object module to produce the executable software component. The third object module includes control information, having a boolean flag which is used to determine which version of said software component is executed. The boolean flag has a default value which provides the first software version for initial execution.
According to yet another aspect, the invention provides a software component for use in a computer system, wherein said software component is used by applications running on the computer system, and wherein said software component comprises: a first and a second version, wherein said first version of the software component is optimised and said second version contains debug code for providing trace information, and means for selectively switching between the first and second versions of said software component, said switching being transparent to the applications already using the software component.
According to a yet further aspect the invention provides a computer system for executing a software component, wherein said software component is used by applications running on the computer system, and wherein said computer system comprises: means for providing a first and second version of said software component, wherein said first version of the software component is optimised and said second version contains debug code for providing trace information, and wherein said computer system further comprises: means for selectively switching between the first and second versions of said software component, said switching being transparent to those applications already using the software component.